package com.wang.tool.monitor.core;

import com.alibaba.fastjson.JSON;
import com.wang.tool.common.ErrorInfo;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.callback.GlobalMonitorCallback;
import com.wang.tool.monitor.fastover.FastoverInfo;
import com.wang.tool.monitor.ops.MonitorOps;
import com.wang.tool.monitor.ops.MonitorOpsCenter;
import com.wang.tool.monitor.ops.OpsContext;
import com.wang.tool.monitor.weaver.MonitorWeaver;
import com.wang.tool.monitor.weaver.TypeWeaver;
import com.wang.tool.monitor.weaver.TypeWeaverFactory;
import com.wang.tool.monitor.writer.GlobalMonitorWriter;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 监控信息
 *
 * @Author wangshaopeng
 * @Date 2022/6/27
 */
public class MonitorInfo<R> {

    /**
     * 建议状态码
     * 正常
     */
    public static final String OK = "OK";

    /**
     * 建议状态码
     * 空结果(null)
     */
    public static final String NULL = "NULL";

    /**
     * 建议状态码
     * 集合/map 大小
     */
    public static final String EMPTY = "EMPTY";

    /**
     * 异常
     */
    public static final String ERROR = "_error";

    /**
     * 入参
     */
    public static final String PARAM = "_param";

    /**
     * 出参
     */
    public static final String RESULT = "_result";

    /**
     * 提示
     */
    public static final String TIPS = "_tips";


    /**
     * monitorOps
     */
    public static final String MOPS = "_ops";

    /**
     * REQ_ID
     */
    public static final String REQ_ID = "_req_id";

    /**
     * TIME
     */
    public static final String TIME = "_time";

    public static final String TAG_MONITOR_ERROR = "MonitorError";
    public static final String TAG_FASTOVER = "Fastover";
    public static final String TAG_NOT_EXPECT_STATUS = "NotExpectStatus";
    public static final String TAG_PRE_SET_STATUS = "PreSetStatus";


    /**
     * 链路数据
     * 非空
     */
    private final LinkInfo link;

    /**
     * weaver
     */
    private final MonitorWeaver<R> weaver;


    /**
     * 控制台
     *
     * @see MonitorOpsCenter
     */
    private final MonitorOps ops;

    /**
     * 控制台环境
     *
     * @see MonitorInfo#ops
     */
    private final OpsContext opsContext;

    /**
     * 快速结束信息
     */
    private final FastoverInfo fastoverInfo;

    /**
     * 慢调用阈值
     * weaver赋值
     *
     * @see PerformanceEnum#SLOW
     */
    private Integer slowTime;


    /**
     * 超时调用阈值
     * 一般在外放接口中我们有时无法感知外部超时时间，所以需要主动设置超时阈值
     * weaver赋值
     *
     * @see PerformanceEnum#TIMEOUT
     */
    private Integer outTime;

    /**
     * 入参 可能为空
     * 不用weaver赋值
     */
    private final Object[] params;

    /**
     * 返回值
     * 不用weaver赋值
     */
    private R result;

    /**
     * 详情数据
     * weaver赋值
     * 详情数据,主要是排查用
     */
    private Map<String, Object> details;


    /**
     * 根据下标和类型取参数
     * 强转
     *
     * @param index 下标 0开始
     * @param type  类型
     * @param <T>
     * @return
     */
    public <T> T getParam(int index, Class<T> type) {
        return params == null ? null : (T) params[index];
    }

    /**
     * 添加摘要
     *
     * @param name
     * @param val
     * @return
     */
    public Digest addDigest(String name, Object val) {
        return link.addDigest(name, val);
    }

    /**
     * 添加标签
     * 此添加标签不会打印日志
     *
     * @return
     */
    public void addTag(String tag, String v1, String v2) {
        link.addTag(tag, v1, v2);
    }


    /**
     * 标记是否需要debug
     */
    public void markDebug(boolean debug) {
        link.markDebug(debug);
    }


    /**
     * 添加详情
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     */
    public void addDetail(String key, Object val) {
        if (key == null) {
            return;
        }
        val = val == null ? LinkLogUtil.EMPTY_PLACEHOLDER : val;
        details = details != null ? details : new LinkedHashMap<>(4);
        details.put(key, val);
    }

    /**
     * debug情况下添加详情
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     */
    public void addDetailOnDebug(String key, Object val) {
        if (key == null || !link.isDebug()) {
            return;
        }
        val = val == null ? LinkLogUtil.EMPTY_PLACEHOLDER : val;
        details = details != null ? details : new LinkedHashMap<>(4);
        details.put(key, val);
    }

    /**
     * 添加参数
     *
     * @param param 参数
     */
    public void addDetailParam(Object param) {
        addDetail(PARAM, param);
    }

    /**
     * 添加结果
     *
     * @param result 结果
     */
    public void addDetailResult(Object result) {
        addDetail(RESULT, result);
    }


    /**
     * 添加提示信息
     * 某些场景下服务端想返回一些提示信息到结果中
     * 这时子节点可以分别记录自己的提示信息到自己的载荷中
     * 后面由某节点统一搜取并构建结果吐出去
     *
     * @param tips
     * @see MonitorInfo#buildTipsMap
     */
    public void addTips(String... tips) {
        if (tips == null) {
            return;
        }
        details = details != null ? details : new LinkedHashMap<>(4);
        try {
            List<String> tipList = (List<String>) details.computeIfAbsent(TIPS, k -> new ArrayList<>());
            tipList.addAll(Arrays.asList(tips));
        } catch (Throwable ignore) {
        }
    }

    /**
     * 获取当前节点的提示信息
     *
     * @return
     */
    public List<String> fetchCurrentTips() {
        if (details == null) {
            return null;
        }
        try {
            return (List<String>) details.get(TIPS);
        } catch (Throwable ignore) {
            return null;
        }
    }

    /**
     * 构建提示信息map
     * 注意:递归搜寻,不建议一个函数中多次调用
     * eg:result.put("提示信息",monitorInfo.buildTipsMap())
     *
     * @return 非空 key=link.id
     */
    public Map<String, List<String>> buildTipsMap() {
        return appendTips(new HashMap<>());
    }


    private Map<String, List<String>> appendTips(Map<String, List<String>> map) {
        if (details != null) {
            try {
                List<String> tips = (List<String>) details.get(TIPS);
                if (tips != null) {
                    map.put(link.getLinkid(), tips);
                }
            } catch (Throwable ignore) {
            }
        }
        link.getSegments().forEach((id, linkInfo) -> linkInfo.getMonitor().appendTips(map));
        return map;
    }


    /**
     * 添加简讯信息
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     * @return
     */
    public void addShortDetail(String key, Object val) {
        link.addShortDetail(key, val);
    }

    /**
     * 添加debug简讯信息
     * 内部会进行debug判断
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     * @return
     */
    public void addShortDetailOnDebug(String key, Object val) {
        link.addShortDetailOnDebug(key, val);
    }

    /**
     * 是否成功
     *
     * @return
     */
    public boolean isSuccess() {
        return link.getError() == null;
    }

    /**
     * 是否失败
     *
     * @return
     */
    public boolean isFail() {
        return link.getError() != null;
    }

    /**
     * 是否是慢 超时也算慢
     *
     * @return
     */
    public boolean isSlow() {
        return link.getPerformance() != PerformanceEnum.NORMAL;
    }

    /**
     * 是否是超时
     *
     * @return
     */
    public boolean isTimeout() {
        return link.getPerformance() == PerformanceEnum.TIMEOUT;
    }


    /**
     * 是否是错/超时
     * 错(抛异常) || 超时
     *
     * @return
     */
    public boolean isFailOut() {
        return link.getError() != null || link.getPerformance() == PerformanceEnum.TIMEOUT;
    }

    /**
     * 是否存在隐形异常
     * 1.使用InvokeTemplate抓取了异常
     * 2.输出了异常日志
     * 3.手动标记
     * 很多接口定义都是Result模型,特别是rpc场景,这时success=false,实际是内部catch了异常
     * 为了更好描述此类场景,特定义了此属性,在各扩展节点可识别
     * true也视为特殊场景
     *
     * @return
     */
    public boolean isImplicitError() {
        return link.isImplicitError();
    }


    /**
     * 是否疑似慢调用
     * 1.自己为slow/time_out会触发自己及上游的疑似慢
     * @return
     */
    public boolean isImplicitSlow() {
        return link.isImplicitSlow();
    }

    /**
     * 判断有没有抓取异常
     * 1.使用InvokeTemplate抓取了异常
     * 2.子节点有异常但是自己没异常
     * @return
     */
    public boolean isCatchError() {
        return link.judgeIsCatchError();
    }

    /**
     * 是否命中采样率
     * @return
     */
    public boolean isSampling(){
        return link.isSampling();
    }


    /**
     * 判断是否需要debug
     * 上游节点需要debug时下游默认认为也需要debug
     * 自己节点也可通过setDebug 进行改写及设置
     *
     * @return
     * @see MonitorWeaver#weaveOnBefore
     */
    public boolean isDebug() {
        return link.isDebug();
    }


    /**
     * 判断是否是根链路
     *
     * @return
     */
    public boolean isRoot() {
        return link.getParent() == null;
    }

    /**
     * 判断有没有子链路
     *
     * @return
     */
    public boolean hasSegments() {
        return !link.getSegments().isEmpty();
    }


    /**
     * 带时区格式化当前时间
     * @param timestamp
     * @return
     */
    public static String formatTimeWithZone(long timestamp) {
        // 从时间戳创建 Instant 对象
        Instant instant = Instant.ofEpochMilli(timestamp);

        // 使用系统默认时区将 Instant 转换为 LocalDateTime
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        // 获取系统默认时区
        ZoneId zoneId = ZoneId.systemDefault();

        // 创建带有时区的 DateTimeFormatter
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

        // 结合 LocalDateTime 和时区进行格式化
        return localDateTime.atZone(zoneId).format(formatter);
    }

    /**
     * 取链路节点总数
     *
     * @return
     */
    public int getNodeNum() {
        return link.getSequence().get() + 1;
    }

    public Integer getSlowTime() {
        return slowTime;
    }

    public void setSlowTime(Integer slowTime) {
        this.slowTime = slowTime;
    }

    public Integer getOutTime() {
        return outTime;
    }

    public void setOutTime(Integer outTime) {
        this.outTime = outTime;
    }

    public String getId() {
        return link.getLinkid();
    }

    public String getGroup() {
        return link.getGroup();
    }

    public String getBiz() {
        return link.getBiz();
    }

    public String getAction() {
        return link.getAction();
    }

    public String getStatus() {
        return link.getStatus();
    }

    public int getCost() {
        return link.getCost();
    }

    public long getBeginTime() {
        return link.getBeginTime();
    }

    public long getEndTime() {
        return link.getEndTime();
    }


    public int getBegin() {
        return link.getBegin();
    }

    public int getEnd() {
        return link.getEnd();
    }

    public void setStatus(String status) {
        link.setStatus(status);
    }

    public PerformanceEnum getPerformance() {
        return link.getPerformance();
    }

    public List<Digest> getDigests() {
        return link.getDigests();
    }

    public List<String> getTags() {
        return link.getTags();
    }

    public Map<String, Object> getExt() {
        return link.getShortDetails();
    }


    public ErrorInfo getError() {
        return link.getError();
    }


    public LinkInfo getRoot() {
        return link.getRoot();
    }

    public LinkInfo getParent() {
        return link.getParent();
    }

    public LinkInfo getLink() {
        return link;
    }


    public R getResult() {
        return result;
    }

    public Map<String, Object> getDetails() {
        return details;
    }


    public MonitorOps getOps() {
        return ops;
    }


    public Object[] getParams() {
        return params;
    }

    public FastoverInfo getFastoverInfo() {
        return fastoverInfo;
    }

    public MonitorWeaver<R> getWeaver() {
        return weaver;
    }

    /*-------------------------分割线-------------------------*/

    /**
     * 初始化并步入
     *
     * @param group      非空
     * @param biz        非空
     * @param action     非空
     * @param weaver     编织器
     * @param params     入参
     * @param beginTime  非空
     * @param asyncModel 是否异步模型,异步模型并非包裹执行,步入和步出是割裂的,所以,步入的后面要还原当前线程link
     */
    public MonitorInfo(String group, String biz, String action, MonitorWeaver<R> weaver, Object[] params, long beginTime, boolean asyncModel) {
        LinkInfo parent = LinkHolder.getCurrent();
        this.link = new LinkInfo(this, parent, parent, group, biz, action, beginTime);
        try {
            this.link.setUpLink();
            long rootBeginTime = parent == null ? beginTime : parent.getRoot().getBeginTime();
            this.params = params;
            //前置织入
            this.weaver = weaver;
            //动态控制台
            this.ops = MonitorOpsCenter.getOps(group, biz, action, rootBeginTime);
            if (this.ops == null) {
                this.opsContext = null;
            } else {
                //有配置
                this.opsContext = new OpsContext(rootBeginTime, group, biz, action, params, link.isDebug());
            }

            //ops
            if (this.ops != null) {
                try {
                    this.slowTime = ops.fetchSlowTime(params);
                    this.outTime = ops.fetchOutTime(params);
                    this.link.markDebug(ops.isDebug(opsContext));
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("opsBefore")
                            .pointLink(link)
                            .addAttachment(MonitorInfo.ERROR, e)
                            .log();
                }
            }

            //weaver,可能会获取ops的超时时间当摘要,在ops后执行
            if (this.weaver != null) {
                try {
                    this.weaver.weaveOnBefore(this);
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("weaveBefore")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .log();
                }
            }

            //快速结束
            this.fastoverInfo = fastover();

        } finally {
            if (asyncModel) {
                this.link.recoverLink();
            }
        }
    }

    /**
     * 创建一个完整执行的monitor
     * 此模式下 fastover扩展无意义,因为结果已经拿到了
     *
     * @param parent    手动指定父节点,不会使用当前线程变量
     * @param group
     * @param biz
     * @param action
     * @param beginTime
     * @param params
     * @param endTime
     * @param result
     * @param error
     * @param weaver
     */
    MonitorInfo(LinkInfo parent, String group, String biz, String action, long beginTime, Object[] params, long endTime, R result, ErrorInfo error, MonitorWeaver<R> weaver) {
        LinkInfo wbLink = LinkHolder.getCurrent();
        //根据当前段信息创建一个自己的段,并将自己当做一个参与者参与父段的链路统计
        this.link = new LinkInfo(this, wbLink, parent, group, biz, action, beginTime);
        try {
            this.link.setUpLink();
            long rootBeginTime = parent == null ? beginTime : parent.getRoot().getBeginTime();
            this.params = params;
            //前置织入
            this.weaver = weaver;
            //动态控制台
            this.ops = MonitorOpsCenter.getOps(group, biz, action, rootBeginTime);
            if (ops == null) {
                this.opsContext = null;
            } else {
                //有配置
                this.opsContext = new OpsContext(rootBeginTime, group, biz, action, params, link.isDebug());
            }

            //ops
            if (this.ops != null) {
                try {
                    this.slowTime = ops.fetchSlowTime(params);
                    this.outTime = ops.fetchOutTime(params);
                    this.link.markDebug(ops.isDebug(this.opsContext));
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("opsBefore")
                            .pointLink(link)
                            .addAttachment(MonitorInfo.ERROR, e)
                            .log();
                }
            }

            //weaver可能会获取ops的超时时间当摘要,在ops后执行
            if (this.weaver != null) {
                try {
                    this.weaver.weaveOnBefore(this);
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("weaveBefore")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .log();
                }
            }


            //直接为空
            this.fastoverInfo = null;

            //直接填充
            this.result = result;
            link.end(error, endTime, slowTime, outTime);

            //结果定制编织器
            try {
                TypeWeaver<R> typeWeaver = TypeWeaverFactory.getTypeWeaver(result);
                if (typeWeaver != null) {
                    typeWeaver.weaver(this);
                }
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(biz + ":" + action)
                        .v2("typeWeaver")
                        .pointLink(link)
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }

            //织入
            if (this.weaver != null) {
                try {
                    this.weaver.weaveOnAfter(this);
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(biz + ":" + action)
                            .v2("weaveAfter")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .log();
                }
            }

            //无敌控制台,这里会依赖weaver的状态码
            if (ops != null) {
                opsContext.setResult(result);
                opsContext.setCost(link.getCost());
                opsContext.setStatus(link.getStatus());
                opsContext.setImplicitError(link.isImplicitError());
                opsContext.setImplicitSlow(link.isImplicitSlow());
                opsContext.setDigests(link.getDigests());
                opsContext.setTags(link.getTags());
                opsContext.setDebug(link.isDebug());

                //附件
                try {
                    Object mops = ops.fetchDetail(opsContext);
                    if (mops != null) {
                        addDetail(MOPS, mops);
                    }
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(link.getBiz() + ":" + link.getAction())
                            .v2("opsFetchAttachments")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .addAttachment("opsContext", JSON.toJSONString(opsContext))
                            .log();
                }
            }

            //后回调
            GlobalMonitorCallback.beforeWrite(this);

            //输出
            GlobalMonitorWriter.write(this);
        } finally {
            this.link.recoverLink();
        }
    }


    /**
     * 标记自己抓取了自己的异常
     * 很多接口定义都是Result模型,特别是rpc场景,这时success=false,实际是内部catch了异常
     * 为了更好描述此类场景,特定义了此属性,在各扩展节点可识别
     * 也视为特殊场景
     * 建议使用方式
     * 1.在TypeWeaver时统一根据结果取值
     * result.isSuccess()==false -> monitor.setStatus(result.getErrorCode()); monitor.setImplicitError(true);
     *
     */
    public void markImplicitError() {
        link.markImplicitError();
    }

    /**
     * 递归标记可能存在慢调用
     */
    public void markImplicitSlow() {
        link.markImplicitSlow();
    }


    /**
     * 尝试快速返回
     * 1.正常执行业务返回null
     *
     * @return
     */
    private FastoverInfo fastover() {
        if (ops == null) {
            return null;
        }
        try {
            return ops.fastover(getRoot().getBeginTime(), getParams());
        } catch (Throwable e) {
            LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                    .v1(opsContext.getBiz() + ":" + opsContext.getBiz())
                    .v2("opsFastover")
                    .addAttachment(ERROR, e)
                    .pointLink(link)
                    .log();
            return null;
        }
    }

    /**
     * 步出
     *
     * @param result     结果
     * @param error      异常
     * @param endTime    时间
     * @param asyncModel 是否异步模型,异步模型并非包裹执行,步入和步出是割裂的,所以,步出的前面要设置当前线程link
     */
    void stepOutMonitor(R result, ErrorInfo error, long endTime, boolean asyncModel) {
        try {
            if (asyncModel) {
                this.link.setUpLink();
            }
            //基础填充
            this.result = result;

            link.end(error, endTime, slowTime, outTime);

            //类型编织器
            try {
                TypeWeaver<R> typeWeaver = TypeWeaverFactory.getTypeWeaver(result);
                if (typeWeaver != null) {
                    typeWeaver.weaver(this);
                }
            } catch (Throwable e) {
                LinkLogUtil.newTagLog(MonitorInfo.TAG_MONITOR_ERROR)
                        .v1(link.getBiz() + ":" + link.getAction())
                        .v2("typeWeaver")
                        .pointLink(link)
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }

            //织入
            if (weaver != null) {
                try {
                    weaver.weaveOnAfter(this);
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(link.getBiz() + ":" + link.getAction())
                            .v2("weaveAfter")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .log();
                }
            }

            //无敌控制台,需要使用weaver的状态码
            if (ops != null) {
                opsContext.setResult(result);
                opsContext.setCost(link.getCost());
                opsContext.setStatus(link.getStatus());
                opsContext.setImplicitError(link.isImplicitError());
                opsContext.setImplicitSlow(link.isImplicitSlow());
                opsContext.setDigests(link.getDigests());
                opsContext.setTags(link.getTags());
                opsContext.setDebug(link.isDebug());

                //detail
                try {
                    Object mops = ops.fetchDetail(opsContext);
                    if (mops != null) {
                        addDetail(MOPS, mops);
                    }
                } catch (Throwable e) {
                    LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                            .v1(link.getBiz() + ":" + link.getAction())
                            .v2("opsFetcherDetails")
                            .pointLink(link)
                            .addAttachment(ERROR, e)
                            .addAttachment("opsContext", JSON.toJSONString(opsContext))
                            .log();
                }

            }

            //后回调
            GlobalMonitorCallback.beforeWrite(this);

            //输出
            GlobalMonitorWriter.write(this);
        } catch (Throwable e) {
            LinkLogUtil.newTagLog(TAG_MONITOR_ERROR)
                    .v1(link.getBiz() + ":" + link.getAction())
                    .v2("stepOutMonitor")
                    .pointLink(link)
                    .addAttachment(ERROR, e)
                    .log();
        } finally {
            //步出侦测
            link.recoverLink();
        }
    }


    /*-------------------------分割线-------------------------*/


    @Override
    public String toString() {
        return "MonitorInfo{" +
                ", ops=" + ops +
                ", params=" + Arrays.toString(params) +
                ", result=" + result +
                ", details=" + details +
                '}';
    }
}
